package myicesscheduler.web;

import static org.junit.Assert.*;

import java.io.File;
import java.util.Calendar;
import java.util.List;

import myicesscheduler.hibernate.HibernateUtilTest;
import myicesscheduler.hibernate.LectureCoordinatorTransaction;
import myicesscheduler.hibernate.LectureNoticeTransaction;
import myicesscheduler.hibernate.LectureSponsorTransaction;
import myicesscheduler.hibernate.LectureTransaction;
import myicesscheduler.hibernate.ReservationTypeTransaction;
import myicesscheduler.hibernate.SpeakerTransaction;
import myicesscheduler.hibernate.TimeSlotTransaction;
import myicesscheduler.hibernate.autogenerated.Lecture;
import myicesscheduler.hibernate.autogenerated.LectureCoordinator;
import myicesscheduler.hibernate.autogenerated.LectureNotice;
import myicesscheduler.hibernate.autogenerated.LectureSponsor;
import myicesscheduler.hibernate.autogenerated.Speaker;
import myicesscheduler.hibernate.autogenerated.TimeSlot;
import myicesscheduler.web.LectureCommand.TimeSlotCommand;
import myicesscheduler.web.MyFileUtil.FileAlreadyUploadedException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.servlet.ModelAndView;

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( loader = MockServletContextWebContextLoader.class, locations = { "/WebContent/WEB-INF/myicesscheduler-servlet.xml" } )
public class AdminLectureControllerTest extends AbstractJUnit4SpringContextTests
{
   private static final String delim = String.valueOf( File.separatorChar );
   private static final String UploadTestFiledata = "context";
   private static final String UploadTestFilename = "test.txt";
   protected final Log logger = LogFactory.getLog( getClass() );

   @BeforeClass
   static public void beforeClass() throws SecurityException, NoSuchFieldException,
         IllegalArgumentException, IllegalAccessException
   {
      System.setProperty( MyFolderUtil.uploadFolder_folderPropertyName, "." + delim + "WebContent"
            + delim + "WEB-INF" + delim + "upload" );
   }

   @AfterClass
   static public void afterClass()
   {
      HibernateUtilTest.restart();
   }

   @Before
   public void before()
   {
      request.setMethod( "GET" );
      LoginController.setToBeAdmin( request.getSession() );
   }

   @After
   public void after()
   {
      try
      {
         MyFileUtil.getSubFolderName( UploadTestFilename, UploadTestFiledata.getBytes() );
      }
      catch( FileAlreadyUploadedException e )
      {
         final String subFolderName = e.getSubFolderName();
         final String pathAfterUpload = e.getPathAfterUpload();
         final boolean bRemoveFile = MyFolderUtil.uploadFolder.removeFile( pathAfterUpload );
         final boolean bRemoveFolder = MyFolderUtil.uploadFolder
               .removeFolderIfEmpty( subFolderName );
         assertTrue( bRemoveFile );
         assertTrue( bRemoveFolder );
      }
   }

   private final AdminLectureController adminLecture = new AdminLectureController();
   private final MockHttpServletRequest request = new MockHttpServletRequest();
   private final MockHttpServletResponse response = new MockHttpServletResponse();

   @Test
   public void HandleRequest_forAdding() throws Exception
   {
      final ModelAndView mv = adminLecture.handleRequest( request, response );
      assertNotNull( mv );
      assertEquals( "adminLectureAdd", mv.getViewName() );
      assertNull( mv.getModel().get( "lectureIdForModify" ) );
   }

   @Test
   public void HandleRequest_forModify() throws Exception
   {
      request.addParameter( AdminLectureController.param_lectureId, "1" );

      final ModelAndView mv = adminLecture.handleRequest( request, response );
      assertNotNull( mv );
      assertEquals( "adminLectureModify", mv.getViewName() );
      assertEquals( (Integer) 1, (Integer) mv.getModel().get( "lectureIdForModify" ) );
   }

   @Test
   public void formBackingObject_forAddLecture() throws Exception
   {
      LectureCommand cmd = (LectureCommand) adminLecture.formBackingObject( request );
      assertNotNull( cmd );
      assertTrue( cmd.isForAddingNewLecture() );
   }

   @Test
   public void OnSubmit_AddAndModifyLecture() throws Exception
   {
      final LectureCommand cmdAdding = getCommand_Add();
      final ModelAndView mvBeforeModify = adminLecture.onSubmit( cmdAdding );
      assertNotNull( mvBeforeModify );

      final Lecture lectureBeforeModify = (Lecture) mvBeforeModify.getModel().get( "targetLecture" );
      assertNotNull( lectureBeforeModify );

      try
      {
         assertTrue( isLectureSameAsCommand( lectureBeforeModify, cmdAdding, 30 ) );
         final LectureCommand cmdModify = getCommand_Modify( lectureBeforeModify.getInternalId() );
         final ModelAndView mvAfterModify = adminLecture.onSubmit( cmdModify );
         assertNotNull( mvAfterModify );

         final Lecture lectureAfterModify = (Lecture) mvAfterModify.getModel()
               .get( "targetLecture" );
         assertNotNull( lectureAfterModify );
         assertEquals( lectureBeforeModify.getInternalId(), lectureAfterModify.getInternalId() );
         assertTrue( isLectureSameAsCommand( lectureAfterModify, cmdModify, 15 ) );

         removeLecture( lectureAfterModify );
      }
      catch( Exception e )
      {
         removeLecture( lectureBeforeModify );
         throw e;
      }
   }

   @Test
   public void formBackingObject_forModifyLecture() throws Exception
   {
      final ModelAndView mv = adminLecture.onSubmit( getCommand_Add() );
      assertNotNull( mv );
      final Lecture newLecture = (Lecture) mv.getModel().get( "targetLecture" );
      assertNotNull( newLecture );
      assertNotNull( LectureTransaction.getLecture( newLecture.getInternalId() ) );

      try
      {
         request.addParameter( AdminLectureController.param_lectureId, String.valueOf( newLecture
               .getInternalId() ) );
         final LectureCommand cmdModify = (LectureCommand) adminLecture
               .formBackingObject_forModifyLecture( request );
         assertNotNull( cmdModify );
         assertFalse( cmdModify.isForAddingNewLecture() );

         cmdModify.setForAddingNewLecture( true );
         assertTrue( cmdModify.equals( getCommand_Add() ) );
      }
      finally
      {
         removeLecture( newLecture );
      }
   }

   private static LectureCommand getCommand_Add()
   {
      final LectureCommand cmd = new LectureCommand();

      cmd.setForAddingNewLecture( true );
      cmd.setSpeakerName( "speakerName" );
      cmd.setSpeakerOrganization( "speakerOrganization" );
      cmd.setTopic( "topic" );
      cmd.setPaperUrl( "http://paperURL.com/paper.pdf" );
      cmd.setDateYear( 2008 );
      cmd.setDateMonth( LectureCommand.Month.Jan );
      cmd.setDateDay( 1 );
      cmd.setTopicAbstract( "topicAbstract" );
      cmd.setNotice( "notice" );
      cmd.setCoordinatorName( "coordinatorName" );
      cmd.setCoordinatorEmail( "coordinator@Email.com" );
      cmd.setCoordinatorPhone( "000-000-0000" );
      cmd.setSponsorName( "sponsor" );
      cmd.setPaperFile( new MockMultipartFile( "URL", UploadTestFilename, "multipart/form-data",
            UploadTestFiledata.getBytes() ) );

      cmd.setTimeSlotIntervalInMinute( 30 );
      final TimeSlotCommand timeSlots[] = cmd.getTimeSlotCommands();
      final int NineOClockAM = 9 * 2;
      timeSlots[ NineOClockAM ].setLocation( "location" );
      timeSlots[ NineOClockAM ].setNote( "note" );
      timeSlots[ NineOClockAM ].setReservationType( ReservationTypeTransaction.getTypeId_lecture() );
      return cmd;
   }

   private static LectureCommand getCommand_Modify( int lectureIdToModify )
   {
      final LectureCommand cmd = new LectureCommand();
      cmd.setLectureIdToModify( lectureIdToModify );
      cmd.setForAddingNewLecture( false );
      cmd.setSpeakerName( "speaker new name" );
      cmd.setSpeakerOrganization( "speakerNewOrganization" );
      cmd.setTopic( "newTopic" );
      //cmdModify.setPaperURL( "http://paperURL.com/newPaper.pdf" );
      cmd.setDateYear( 2008 + 1 );
      cmd.setDateMonth( LectureCommand.Month.Feb );
      cmd.setDateDay( 2 );
      cmd.setTopicAbstract( "newTopicAbstract" );
      cmd.setNotice( "new notice" );
      cmd.setCoordinatorName( "coordinatorNewName" );
      cmd.setCoordinatorEmail( "newCoordinator@Email.com" );
      cmd.setCoordinatorPhone( "000-000-0001" );
      cmd.setSponsorName( "newSponsor" );
      cmd.setPaperFile( null );

      cmd.setTimeSlotIntervalInMinute( 60 );
      final TimeSlotCommand timeSlots[] = cmd.getTimeSlotCommands();

      timeSlots[ 9 ].setLocation( "location" );
      timeSlots[ 9 ].setNote( "note" );
      timeSlots[ 9 ].setReservationType( ReservationTypeTransaction.getTypeId_lecture() );
      return cmd;
   }

   private static boolean isLectureSameAsCommand( Lecture lecture, LectureCommand cmd,
         int nTimeSlots )
   {
      if( 1 != SpeakerTransaction.getSpeakers_byName( cmd.getSpeakerName() ).size() ) return false;
      if( 1 != LectureNoticeTransaction.getLectureNotice( cmd.getNotice() ).size() ) return false;
      if( 1 != LectureSponsorTransaction.getLectureSponsor( cmd.getSponsorName() ).size() )
         return false;
      if( 1 != LectureCoordinatorTransaction.getCoordinator_byName( cmd.getCoordinatorName() )
            .size() ) return false;
      if( 1 != LectureCoordinatorTransaction.getCoordinator_byEmailAndPhone(
            cmd.getCoordinatorEmail(), cmd.getCoordinatorPhone() ).size() ) return false;

      if( null == lecture.getTopic() && null != cmd.getTopic() ) return false;
      if( null == lecture.getTopicAbstract() && null != cmd.getTopicAbstract() ) return false;
      if( null == lecture.getPaperUrl() && null != cmd.getPaperUrl() ) return false;
      if( null != lecture.getTopic() && false == lecture.getTopic().equals( cmd.getTopic() ) )
         return false;
      if( null != lecture.getTopicAbstract()
            && false == lecture.getTopicAbstract().equals( cmd.getTopicAbstract() ) ) return false;
      if( null != lecture.getPaperUrl()
            && false == lecture.getPaperUrl().equals( cmd.getPaperUrl() ) ) return false;

      final Calendar cal1 = Calendar.getInstance();
      final Calendar cal2 = Calendar.getInstance();
      final List< TimeSlot > timeSlots = TimeSlotTransaction.getTimeSlotList( lecture );
      for( final TimeSlotCommand timeSlotCmd : cmd.getTimeSlotCommands() )
      {
         if( false == timeSlotCmd.isEnabled() ) continue;
         cal1.setTime( timeSlotCmd.getBeginDate() );
         boolean bFound = false;
         for( final TimeSlot timeSlot : timeSlots )
         {
            cal2.setTime( timeSlot.getBeginTime() );
            if( false == cal1.equals( cal2 ) ) continue;
            if( bFound ) return false;
            bFound = true;
         }
         if( false == bFound ) return false;
      }

      return true;
   }

   private static boolean removeLecture( Lecture lecture )
   {
      try
      {
         final Speaker speaker = lecture.getSpeaker();
         final LectureSponsor sponsor = lecture.getLectureSponsor();
         final LectureCoordinator coordinator = lecture.getLectureCoordinator();
         final LectureNotice notice = lecture.getLectureNotice();

         TimeSlotTransaction.remove_byLecture( lecture );
         LectureTransaction.remove( lecture );
         SpeakerTransaction.remove( speaker );
         LectureSponsorTransaction.remove( sponsor );
         LectureCoordinatorTransaction.remove( coordinator );
         LectureNoticeTransaction.remove( notice );
      }
      catch( Exception e )
      {
         return false;
      }
      return true;
   }

}
